#include<pthread.h>
#include<queue>
#include<stdio.h>

using namespace std;

class Safqueue
{
    public:
        Safqueue()
        {}

        ~Safqueue()
        {
            pthread_mutex_destroy(&mutex);
            pthread_cond_destroy(&produser_cond);
            pthread_cond_destroy(&consumer_cond);
        }

        int init()
        {
            capacity = 3;
            pthread_mutex_init(&mutex, NULL);
            pthread_cond_init(&produser_cond, NULL);
            pthread_cond_init(&consumer_cond, NULL);
        }
    public:
        void Push(int val, int who)
        {
            pthread_mutex_lock(&mutex);
            while(que.size() >= capacity){
                pthread_cond_wait(&produser_cond, &mutex);
            }
            que.push(val);
            printf("i am producer %d, i produce %d\n", who, val);
            pthread_mutex_unlock(&mutex);
            pthread_cond_signal(&consumer_cond);
        }
        
        int Front()
        {
            pthread_mutex_lock(&mutex);
            while(que.empty()){
                pthread_cond_wait(&consumer_cond, &mutex);
            }

            int ret = que.front();
            return ret;
        }

        void Pop(int who)
        {
            int ret = que.front();
            que.pop();
            printf("i am consumer %d, i consume %d\n", who, ret);
            pthread_mutex_unlock(&mutex);
            pthread_cond_signal(&produser_cond);
        }
        /*
         * 这个pop已经完全可以保证队列的安全性，但我更希望队列在使用时更加符合标准，即将接口和std::que对齐，虽然在生产消费者模型中
         * front和pop的操作是具有原子性的，也就是说这两个操作完全可以融为一体，但我依旧想将他拆开，这样更符合使用习惯 
        int Pop(int who)
        {
            pthread_mutex_lock(&mutex);
            while(que.empty()){
                pthread_cond_wait(&consumer_cond, &mutex);
            }
            
            int ret = que.front();
            que.pop();
			printf("i am consumer %d, i consume %d\n", who, ret);
            pthread_mutex_unlock(&mutex);
            pthread_cond_signal(&produser_cond);
            return ret;
        }
        */ 
        /*
         * 踩坑记录1：
         *      对于消费者来说队列中的元素为临界区，可能同时出现多个消费者想要访问获取这个对头元素的情况，因此应当队消费者间进行互斥操作
         *      eg：现有线程A， 线程B，且cpp中代码为：
         *      int ret = Fornt
         *      que.Pop 
         *      错误原因：我期望线程A获取队头元素然后弹出队头，代表着线程A消费了这个对头元素，但根据我这里的写法，很可能出现，线程A在Fornt时
         *      抢到了锁，但在Pop时没有抢到锁，Pop时被线程B抢到了，因此造成了程序的二异性
         *      解决方案：获取队头元素以及弹出队首这两个操作应当具有原子性
         int Fornt()
         {
            pthread_mutex_lock(&mutex);
            return que.font();
            pthread_mutex_unlock(&mutex);
         }
         */ 
    private:
        queue<int> que;
        int capacity;
        pthread_mutex_t mutex;
        pthread_cond_t produser_cond;
        pthread_cond_t consumer_cond;
};
